#!/usr/bin/env python
# encoding: utf-8


"""
@file: hancan_zuizhi_tiaojian.py
@time: 2017/4/24 下午4:47
"""
# 可行域中含参数的线性规划问题

from itertools import chain, permutations
from mathsolver.functions.root.jiefangchenzu import *
from mathsolver.functions.budengshi import common_opers as co
from operator import itemgetter
from mathsolver.functions.budengshi.xianxingguihua.xianxingguihua import XianXingGuiHua


# 含参 线性规划的最值之间的关系
# style1 Input: paramer1:不等式组; paramer2: 目标函数表达式; paramer3:eqs(最值关系， 给定最大值max_value， 或最小值min_value, 或者他们之间的关系) ouput 参数值
# 例子
class HanCanXianXingGuiHuaZuiZhiTiaoJian(BaseFunction):
    def solver(self, *args):
        self.label.add('含参线性规划求参数值')
        arg1 = args[0]
        arg2 = args[1]
        _z, f_expr = arg2.sympify()
        arg3 = args[2]
        inter_points = co.satisfied_ineqs_intersections(arg1)  # 满足条件的交点
        inter_points = map(itemgetter(1), inter_points)
        points = map(lambda _: (_[0][1], _[1][1]), inter_points)
        self.steps.append(['由题意可知求出交点:', ''])
        step1 = ''
        for index, p in enumerate(points):
            if index == 0:
                step1 += '(' + new_latex(p[0]) + ',' + new_latex(p[1]) + ')'
            else:
                step1 += ', ' + '(' + new_latex(p[0]) + ',' + new_latex(p[1]) + ')'
        self.steps.append([step1, ''])
        op_symbs = co.eqs_symbs(arg3.sympify())  # 等式中是否含有 max_value  和 min_value
        if len(op_symbs) == 2:  # 给了最大值 和最小值的关系
            mx_symb = sympify('max_value')
            mi_symb = sympify('min_value')
        else:
            if str(op_symbs[0]) == 'max_value':  # 只给了最大值
                mx_symb = sympify('max_value')
                mi_symb = None
            elif str(op_symbs[0]) == 'min_value':  # 只给了最小值
                mx_symb = None
                mi_symb = sympify('min_value')
            else:
                raise Exception('illegal maximum or minimum symbols')
        op_pers = permutations(inter_points, len(op_symbs))
        pvs = []  # 参数的取值
        last_max_point = None
        last_min_point = None
        if mx_symb and mi_symb:  # 如果存在的是max_value 和 min_value之间的关系
            eqs = arg3.sympify()  # 最值等式组
            param_symb = None
            for mx_v_p, mi_v_p in op_pers:  # 假设 第一个 为最大值，第二个为最小值
                mx_p = (mx_v_p[0][1], mx_v_p[1][1])
                mi_p = (mi_v_p[0][1], mi_v_p[1][1])
                mx_v = f_expr.subs(mx_v_p)  # 假设为取最大值的交点时，表达式的值
                mi_v = f_expr.subs(mi_v_p)  # 假设为取最小值的交点时，表达式的值
                subs = ((mx_symb, mx_v), (mi_symb, mi_v))
                eqs = map(lambda _: [_[0].subs(subs), _[1].subs(subs)], eqs)
                param_vs = co.isolve_eqs(BaseEqs(eqs))  # 解出参数的值
                for parma_v in param_vs:  # 开始验证
                    param_symb, pv = parma_v[0]
                    parm_subs = (param_symb, pv)
                    mx_v_p_v = (mx_p[0].subs(*parm_subs), mx_p[1].subs(*parm_subs))  # 此时最大值坐标的坐标值
                    mi_v_p_v = (mi_p[0].subs(*parm_subs), mi_p[1].subs(*parm_subs))  # 此时最小值坐标的坐标值
                    points_vs = map(lambda _p: (_p[0].subs(*parm_subs), _p[1].subs(*parm_subs)), points)
                    expr_values = map(lambda _: (_, f_expr.subs('x', _[0]).subs('y', _[1])),
                                      points_vs)  # 目标表达式在每个坐标点上的取值
                    max_p = max(expr_values, key=itemgetter(1))[0]
                    min_p = min(expr_values, key=itemgetter(1))[0]
                    if not (max_p == mx_v_p_v and min_p == mi_v_p_v):
                        continue
                    ineqs2 = map(lambda _: [_[0].subs(*parm_subs), _[1], _[2].subs(*parm_subs)], arg1.sympify())
                    f2 = f_expr.subs(*parm_subs)
                    get_mx_value = list(XianXingGuiHua().solver(BaseIneqs(ineqs2), BaseEq(['z', f2]), '最大值').output[
                                            0].sympify().values()[0])[0]
                    get_mi_value = list(XianXingGuiHua().solver(BaseIneqs(ineqs2), BaseEq(['z', f2]), '最小值').output[
                                            0].sympify().values()[0])[0]
                    get_op_subs = ((mx_symb, get_mx_value), (mi_symb, get_mi_value))
                    eqs2 = map(lambda _: [_[0].subs(param_symb, pv).subs(get_op_subs),
                                          _[1].subs(param_symb, pv).subs(get_op_subs)], eqs)
                    eqs3 = filter(lambda _: _[0] == _[1], eqs2)
                    if len(eqs3) == len(eqs2):
                        last_max_point = mx_p
                        last_min_point = min_p
                        pvs.append(pv)
            last_param_v = pvs[0]
            last_f = BaseEq(map(lambda _: _.subs(param_symb, last_param_v), arg2.sympify()))
            self.steps.append(
                ['当' + BaseEq([param_symb, last_param_v]).printing() + '时满足条件, 目标表达式' + last_f.printing(), ''])
            self.steps.append(['在' + co.point_print(last_max_point) + '取得最大值', ';'])
            self.steps.append(['在' + co.point_print(last_min_point) + '取得最小值', ';'])
            self.output.append(BaseSymbolValue({param_symb: last_param_v}))
            return self
        elif len(op_symbs) == 1:  # 如果只有一个变量 给定最大值或者最小值
            op_symb = op_symbs[0]
            op_type = 'max' if str(op_symb) == 'max_value' else 'min'  # 最值类型
            op_func = max if op_type == 'max' else min
            op_text = '最大值' if op_type == 'max' else '最小值'
            last_op_point = None
            param_symb = None
            for op_point in inter_points:  # 假设当前为最值
                x_point, y_point = op_point
                op_p = (x_point[1], y_point[1])  # 当前坐标点
                op_point_v = f_expr.subs(op_point)
                subs = (op_symb, op_point_v)
                eqs = map(lambda _: [_[0].subs(*subs), _[1].subs(*subs)], arg3.sympify())  # 最值条件等式
                if op_point_v.is_real:  # 如果不用求参数，可以直接求出参数的值 那么直接带入最值条件验证
                    eqs2 = filter(lambda _: (not _[0].is_real) or (not _[1].is_real) or _[0] == _[1], eqs)
                    eqs3 = filter(lambda _: (not _[0].is_real) or (not _[1].is_real), eqs)  # 含有参数的等式
                    if len(eqs2) != len(eqs):  # 验证不通过
                        continue
                    if eqs3:  # 如果有含参等式
                        param_vs = co.isolve_eqs(BaseEqs(eqs3))  # 解出参数的值

                else:
                    param_vs = co.isolve_eqs(BaseEqs(eqs))  # 解出参数的值
                # 开始验证
                for parma_v in param_vs:  # 开始验证
                    param_symb, pv = parma_v[0]
                    parm_subs = (param_symb, pv)
                    points_vs = map(lambda _p: (_p[0].subs(*parm_subs), _p[1].subs(*parm_subs)), points)
                    expr_values = map(lambda _: (_, f_expr.subs('x', _[0]).subs('y', _[1])),
                                      points_vs)  # 目标表达式在每个坐标点上的取值
                    expr_op_p = op_func(expr_values, key=itemgetter(1))[0]
                    op_p_v = (op_p[0].subs(*parm_subs), op_p[1].subs(*parm_subs))
                    if expr_op_p != op_p_v:  # 如果不相等则不符合
                        continue
                    ineqs2 = map(lambda _: [_[0].subs(*parm_subs), _[1], _[2].subs(*parm_subs)], arg1.sympify())
                    f2 = f_expr.subs(*parm_subs)
                    get_op_value = list(XianXingGuiHua().solver(BaseIneqs(ineqs2), BaseEq(['z', f2]), op_text).output[
                                            0].sympify().values()[0])[0]
                    get_op_subs = (op_symb, get_op_value)
                    eqs2 = map(lambda _: [_[0].subs(param_symb, pv).subs(*get_op_subs),
                                          _[1].subs(param_symb, pv).subs(*get_op_subs)], eqs)
                    eqs3 = filter(lambda _: _[0] == _[1], eqs2)
                    if len(eqs3) == len(eqs2):
                        pvs.append(pv)
                        last_op_point = op_p_v
            last_param_v = pvs[0]
            last_f = BaseEq(map(lambda _: _.subs(param_symb, last_param_v), arg2.sympify()))
            self.steps.append(
                ['当' + BaseEq([param_symb, last_param_v]).printing() + '时满足条件, 目标表达式' + last_f.printing(), ''])
            self.steps.append(['在' + co.point_print(last_op_point) + '取得' + op_text, '.'])
            self.output.append(BaseSymbolValue({param_symb: last_param_v}))
            return self
        else:
            raise Exception('illegal input')


if __name__ == '__main__':
    # _solve = HanCanXianXingGuiHuaZuiZhiTiaoJian().solver(
    #     BaseIneqs([['y', '>=', 'x'], ['x + y', '<=', '2'], ['x', '>=', 'a']]),
    #     BaseEq(['z', '2*x + y']), BaseEqs([['max_value', '4*min_value']]))

    _solve = HanCanXianXingGuiHuaZuiZhiTiaoJian().solver(
        BaseIneqs([['x + y', '>=', '0'], ['x - 2*y + 2', '>=', '0'], ['m*x - y', '<=', '0']]),
        BaseEq(['z', '2*x - y']), BaseEqs([['max_value', '2']]))
